﻿using System.Collections.Generic;
using SdmxMl.Common;
using SdmxMl.Manager;
using SdmxMl.Structure;
using System.Linq;
using Ch.Admin.Bfs.Sms.Sdmx.Extension.CodeBook.Configuration;

namespace Ch.Admin.Bfs.Sms.Sdmx.Extension.CodeBook.Meta
{
    public class BaseMetaProvider : IMetaProvider
    {
        #region Internal Members

        public static SmManager sm;
        protected static RefVarHelper refHelper;
        protected static Dictionary<string, CodeListType> dicoCodelist;

        protected string fullIdKey;
        protected string lg;
        protected Hierarchy hierarchy;

        #endregion

        /// <summary>Currently loaded book configuration options </summary>
        public BookConfiguration BookConfig { get; protected set; }


        #region static constructor

        static BaseMetaProvider()
        {
            sm = new SmManager();
            refHelper = new RefVarHelper(sm);
            dicoCodelist = new Dictionary<string, CodeListType>();
        }
        #endregion

        #region Constructor

        public BaseMetaProvider(string lang, string metaFullId, BookConfiguration cfg)
        {
            lg = lang;
            fullIdKey = metaFullId;
            BookConfig = cfg;
        }
        #endregion

        #region Static Methods

        public static void CodelistClear()
        {
            dicoCodelist.Clear();
        }

        public static HierarchyNode ReferedCodelistModuleContent()
        {
            HierarchyNode hnModuleType = new HierarchyNode(TableOfContents.Codelists.ToString(), null, (int)TableOfContents.Codelists);

            foreach (KeyValuePair<string, CodeListType> kvp in dicoCodelist)
                hnModuleType.Nodes.Add(new HierarchyNode(kvp.Key, string.Empty, (int)TableOfContents.Codelists));
            return hnModuleType;
        }
        #endregion

        protected List<ISdmxHierarchyListItem> GetChildList(string parentId, List<ISdmxHierarchyListItem> listItems)
        {
            List<ISdmxHierarchyListItem> listChild = new List<ISdmxHierarchyListItem>();

            foreach (ISdmxHierarchyListItem item in listItems)
            {
                if (item.Parent == parentId)
                    listChild.Add(item);
            }

            return listChild;
        }

        #region IMetaProvider

        /// <summary> Maintenable artefact meta fullId (Agency+Id+version)</summary>
        public string FullIdKey { get { return fullIdKey; } }

        /// <summary>Node holding root info of maintenable artefact, hierarchy represented in child nodes</summary>
        public HierarchyNode GetRootNode()
        {
            return hierarchy != null && hierarchy.Count > 0 ? hierarchy[0] : null;
        }

        // List of nodes presenting Annotations info (ID = Type, Name = Text, Descr = Title)
        public List<HierarchyNode> GetAnnotations(HierarchyNode node)
        {
            IAnnotationList annList = node.Tag as IAnnotationList;
            List<HierarchyNode> list = new List<HierarchyNode>();
            foreach (Annotation a in annList.AnnotationList)
            {
                // Ignore annotation type that are in filter
               if (BookConfig.AnnotIgnoreList.Find(x => x == a.Type) == null)
                {
                    list.Add(new HierarchyNode(BookConfig.GetTranslatedAnnotationType(a.Type, lg), a.Text.GetForLanguage(lg), a.Title, a));
                }
            }

            return list;
        }

        public string GetVariableType(HierarchyNode node)
        {
            ConceptType c = node.Tag as ConceptType;
            return GetType(c);

        }


        public List<KeyValuePair<string, string>> GetTypeDetail(HierarchyNode node)
        {
            List<KeyValuePair<string, string>> List = new List<KeyValuePair<string, string>>();
            ConceptType c = node.Tag as ConceptType;
            if (c != null && c.TextFormat.TextType != TextTypeType.Undefined)
            {
                List.Add(new KeyValuePair<string, string>("Type", c.TextFormat.TextType.ToString()));
                if (c.TextFormat.minLength.Length > 0)
                    List.Add(new KeyValuePair<string, string>("Min. Length", c.TextFormat.minLength));
                if (c.TextFormat.maxLength.Length > 0)
                    List.Add(new KeyValuePair<string, string>("Max. Length", c.TextFormat.maxLength));
                if (c.TextFormat.IsNumeric)
                {
                    if (c.TextFormat.startValue.Length > 0)
                        List.Add(new KeyValuePair<string, string>("Start Value", c.TextFormat.startValue));
                    if (c.TextFormat.endValue.Length > 0)
                        List.Add(new KeyValuePair<string, string>("End Value", c.TextFormat.endValue));
                    if (c.TextFormat.decimals.Length > 0)
                        List.Add(new KeyValuePair<string, string>("Decimal", c.TextFormat.decimals));
                }
                if (c.TextFormat.pattern.Length > 0)
                    List.Add(new KeyValuePair<string, string>("Pattern", c.TextFormat.pattern));

            }
            return List;
        }

        public HierarchyNode GetCodelistReference(HierarchyNode node)
        {
            HierarchyNode codelistNode = null;
            ConceptType c = node.Tag as ConceptType;
            if (c != null && c.CoreRepresentation.IsFilled)
            {
                CodeListType clt = refHelper.GetCodelist(c.CoreRepresentation);
                if (dicoCodelist.ContainsKey(clt.FullIdent) == false)
                    dicoCodelist.Add(clt.FullIdent, clt);
                codelistNode = new HierarchyNode(clt.FullIdent, clt.Name.GetForLanguage(lg), clt);
            }
            return codelistNode;
        }

        #endregion

        /// <summary>Get a textual representation of assigned type. Indicate 'Codelist' if any is associated</summary>
        /// <param name="c">the concept to look for</param>
        /// <returns>type representation string</returns>
        protected string GetType(ConceptType c)
        {
            string type = string.Empty;
            if (c != null)
            {
                if (c.CoreRepresentation.IsFilled)
                    type = "Codelist";
                else if (c.TextFormat.TextType != TextTypeType.Undefined)
                    type = c.TextFormat.TextType.ToString();
            }
            return type;
        }

        #region codelist

        protected HierarchyNode ProduceCodelistHierarchy(CodeListType clt, bool longTxt)
        {
            bool hierarchyHasdetail = false;
            HierarchyNode root = new HierarchyNode(clt.FullIdent, clt.Name.GetForLanguage(lg), clt.Description.GetForLanguage(lg), clt);
            List<ISdmxHierarchyListItem> listItems = new List<ISdmxHierarchyListItem>(clt.CodeList);
            FillCodelistRecurse(root.Nodes, listItems, "", ref hierarchyHasdetail, longTxt);
            root.HasDetail = hierarchyHasdetail;
            return root;
        }

        private void FillCodelistRecurse(Hierarchy nodes, List<ISdmxHierarchyListItem> listItems, string parentId, ref bool hierarchyHasdetail, bool longTxt)
        {
            List<ISdmxHierarchyListItem> listChild = GetChildList(parentId, listItems);
            foreach (ISdmxHierarchyListItem item in listChild)
            {
                CodeType c = item as CodeType;
                string name = item.Name.GetForLanguage(lg);
                
                // Specific questionnaire option (Usage of long text instead of official name of code)
                if (longTxt)
                {
                    Annotation a = c.AnnotationList.GetAnnotationType("LONG_TXT");
                    if (a != null && a.Text.GetForLanguage(lg).Trim().Length > 0)
                        name = a.Text.GetForLanguage(lg);
                }

                HierarchyNode childNode = new HierarchyNode(item.Id, name, c.Description.GetForLanguage(lg), item);

                //Determines if code has detail
                childNode.HasDetail = c.Description.GetForLanguage(lg).Length > 0 || FilteredListCount(c.AnnotationList) > 0;
                if (childNode.HasDetail)
                    hierarchyHasdetail = true;
                nodes.Add(childNode);
                listItems.Remove(item);
                FillCodelistRecurse(childNode.Nodes, listItems, item.Id, ref hierarchyHasdetail, longTxt);
            }
        }

        /// <summary>
        /// Reports count of annotation not filtered by config option
        /// </summary>
        /// <param name="annList">the list of annotation (unfiltered</param>
        /// <returns>the count once filtered</returns>
        int FilteredListCount(AnnotationList annList)
        {
            int count = 0;

            foreach (Annotation a in annList)
            {
                // Ignore annotation type that are in filter
                if (BookConfig.AnnotIgnoreList.Find(x => x == a.Type) == null)
                {
                    ++count;
                }
            }
            return count;
        }

        #endregion

    }
}
